<?php

declare(strict_types=1);

namespace App\Tests\Functional;

use App\Tests\Support\FunctionalTester;
use HttpSoft\Message\ServerRequest;
use Yiisoft\Http\Method;
use Yiisoft\Http\Status;

use function PHPUnit\Framework\assertMatchesRegularExpression;
use function PHPUnit\Framework\assertSame;
use function PHPUnit\Framework\assertStringContainsString;
use function PHPUnit\Framework\assertStringNotContainsString;

final class WebhookCest
{
    public function createAndCapture(FunctionalTester $I): void
    {
        $I->resetWebhookTables();

        $createRequest = (new ServerRequest(
            method: Method::POST,
            uri: '/webhooks',
        ))->withParsedBody([
            'label' => '测试用 Hook',
        ]);

        $createResponse = $I->sendRequest($createRequest);

        assertSame(Status::SEE_OTHER, $createResponse->getStatusCode());

        $location = $createResponse->getHeaderLine('Location');
        assertMatchesRegularExpression('#^/webhooks/(?P<token>[0-9a-f\-]{36})$#', $location);
        preg_match('#^/webhooks/(?P<token>[0-9a-f\-]{36})$#', $location, $matches);
        $token = $matches['token'] ?? '';

        $showResponse = $I->sendRequest(new ServerRequest(uri: $location));
        assertSame(Status::OK, $showResponse->getStatusCode());
        $showBody = $showResponse->getBody()->getContents();
        assertStringContainsString('测试用 Hook', $showBody);

        $incomingRequest = new ServerRequest(
            method: Method::POST,
            uri: '/hook/' . $token . '?hello=world',
            headers: [
                'Content-Type' => 'application/json',
                'User-Agent' => 'Codeception',
            ],
            body: json_encode(['foo' => 'bar'], JSON_THROW_ON_ERROR),
            serverParams: [
                'REMOTE_ADDR' => '127.0.0.1',
            ],
        );

        $incomingResponse = $I->sendRequest($incomingRequest);
        assertSame(Status::OK, $incomingResponse->getStatusCode());
        assertSame('application/json', $incomingResponse->getHeaderLine('Content-Type'));

        $detailResponse = $I->sendRequest(new ServerRequest(uri: $location));
        $detailBody = $detailResponse->getBody()->getContents();
        assertStringContainsString('"foo": "bar"', $detailBody);
        assertStringContainsString('/hook/' . $token, $detailBody);

        $updateResponse = $I->sendRequest(
            (new ServerRequest(method: Method::POST, uri: '/webhooks/' . $token . '/response'))
                ->withParsedBody([
                    'status' => '201',
                    'content_type' => 'text/plain',
                    'headers' => "X-Debug: yes\nCache-Control: no-cache",
                    'body' => 'ok',
                ]),
        );

        assertSame(Status::SEE_OTHER, $updateResponse->getStatusCode());

        $customRequest = new ServerRequest(
            method: Method::GET,
            uri: '/hook/' . $token . '/custom/path',
        );

        $customResponse = $I->sendRequest($customRequest);
        assertSame(201, $customResponse->getStatusCode());
        assertSame('text/plain', $customResponse->getHeaderLine('Content-Type'));
        assertSame('yes', $customResponse->getHeaderLine('X-Debug'));
        assertSame('no-cache', $customResponse->getHeaderLine('Cache-Control'));
        assertSame('ok', $customResponse->getBody()->getContents());

        $finalPage = $I->sendRequest(new ServerRequest(uri: $location));
        $finalBody = $finalPage->getBody()->getContents();
        assertStringContainsString('/custom/path', $finalBody);

        $clearResponse = $I->sendRequest(
            new ServerRequest(method: Method::POST, uri: '/webhooks/' . $token . '/requests/clear'),
        );

        assertSame(Status::SEE_OTHER, $clearResponse->getStatusCode());

        $afterClearResponse = $I->sendRequest(new ServerRequest(uri: $location));
        $afterClearBody = $afterClearResponse->getBody()->getContents();
        assertStringContainsString('还没有收到任何请求', $afterClearBody);
        assertStringNotContainsString('/custom/path', $afterClearBody);

        $deleteResponse = $I->sendRequest(
            new ServerRequest(method: Method::POST, uri: '/webhooks/' . $token . '/delete'),
        );

        assertSame(Status::SEE_OTHER, $deleteResponse->getStatusCode());
        assertSame('/', $deleteResponse->getHeaderLine('Location'));

        $afterDeleteResponse = $I->sendRequest(new ServerRequest(uri: $location));
        assertSame(Status::NOT_FOUND, $afterDeleteResponse->getStatusCode());
    }
}
